package guoan.service;

import guoan.core.dto.DataGridModel;
import guoan.core.dto.PageResponse;
import guoan.core.persistence.DynamicSpecifications;
import guoan.core.persistence.SearchFilter;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;

/**
 * 分页专用的Serivce
 * 
 * @author HeyMan
 *
 * @param <T>
 *            T 实体
 */
public class PageSerivce<T> {

	@PersistenceContext
	private EntityManager entityManager;

	/**
	 * 构建 Specification
	 * 
	 * @param searchParams
	 *            查询参数MAP
	 * @return
	 */
	public Specification<T> buildSpecification(Map<String, ? extends Object> searchParams) {
		Map<String, SearchFilter> filters = SearchFilter.parse(searchParams);
		return DynamicSpecifications.bySearchFilter(filters.values());
	}

	/**
	 * 构建 PageRequest
	 * 
	 * @param dgm
	 *            分页参数
	 * @return
	 */
	public PageRequest buildPageRequest(DataGridModel dgm) {
		Sort sort = null;
		if (!Strings.isNullOrEmpty(dgm.getOrder()) && !Strings.isNullOrEmpty(dgm.getSort())) {
			if ("asc".equalsIgnoreCase(dgm.getOrder())) {
				sort = new Sort(Direction.ASC, dgm.getSort());
			} else {
				sort = new Sort(Direction.DESC, dgm.getSort());
			}
		}
		return new PageRequest(dgm.getPage(), dgm.getRows(), sort);
	}

	/**
	 * 构建 PageResponse
	 * 
	 * @param page
	 *            PageRequest
	 * @param draw
	 *            dataTable 专用draw
	 * @return
	 */
	public PageResponse<T> formatPageToPageResponse(Page<T> page, int draw) {
		return new PageResponse<T>(page.getContent(), page.getTotalElements(), draw);
	}

	/**
	 * 最简单的分页查询封装,返回实体的PageResponse
	 * 
	 * @param pa
	 *            查询DAO
	 * @param paramMap
	 *            查询参数
	 * @param dgm
	 *            分页封装
	 * @return PageResponse<T>
	 */
	public PageResponse<T> pageResponse(JpaSpecificationExecutor<T> pa, Map<String,? extends Object> paramMap, DataGridModel dgm) {
		Page<T> page = pa.findAll(buildSpecification(paramMap), buildPageRequest(dgm));
		return new PageResponse<T>(page.getContent(), page.getTotalElements(), dgm.getDraw());
	}

	/**
	 * 最简单的分页查询封装,返回实体的PageResponse
	 * 
	 * @param pa
	 *            查询DAO
	 * @param paramMap
	 *            查询参数
	 * @param dgm
	 *            分页封装
	 * @return PageResponse<T>
	 */
	public PageResponse<T> pageResponse(JpaSpecificationExecutor<T> pa, DataGridModel dgm) {
		Map<String, Object> searchParams = Maps.newHashMap();
		Page<T> page = pa.findAll(buildSpecification(searchParams), buildPageRequest(dgm));
		return new PageResponse<T>(page.getContent(), page.getTotalElements(), dgm.getDraw());
	}

	/**
	 * 返回数组形式的分页数据集合
	 * 
	 * @param sql
	 *            语句
	 * @param dgm
	 *            分页参数
	 * @return PageResponse<Object[]>
	 */
	public PageResponse<Object[]> queryPageByJpql(String sql, DataGridModel dgm) {
		return queryPageByJpql(sql, dgm, null);
	}

	/**
	 * 返回数组形式的分页数据集合
	 * 
	 * @param sql
	 *            语句
	 * @param dgm
	 *            分页参数
	 * @param paramMap
	 *            param
	 * @return PageResponse<Object[]>
	 */
	@SuppressWarnings("unchecked")
	public PageResponse<Object[]> queryPageByJpql(String sql, DataGridModel dgm, Map<String, ? extends Object> paramMap) {
		Long count = getCount(sql, paramMap).longValue();
		Query query = setParam(
				getEntityManager().createNativeQuery(sql).setFirstResult(dgm.getStart())
						.setMaxResults(dgm.getStart() + dgm.getLength()), paramMap);
		List<Object[]> result = query.getResultList();
		return new PageResponse<Object[]>(result, count, dgm.getDraw());
	}

	/**
	 * 返回实体式的分页数据集合
	 * 
	 * @param sql
	 *            查询语句
	 * @param dgm
	 *            分页参数
	 * @param resultClass
	 *            实体类型
	 * @return PageResponse<T>
	 */
	public PageResponse<T> queryPageByJpqlForEntity(String sql, DataGridModel dgm, Class<T> resultClass) {
		return queryPageByJpqlForEntity(sql, dgm, resultClass, null);
	}

	/**
	 * 返回实体式的分页数据集合
	 * 
	 * @param sql
	 *            查询语句
	 * @param dgm
	 *            分页参数
	 * @param resultClass
	 *            实体类型
	 * @param paramMap
	 *            查询参数
	 * @return PageResponse<T>
	 */
	@SuppressWarnings("unchecked")
	public PageResponse<T> queryPageByJpqlForEntity(String sql, DataGridModel dgm, Class<T> resultClass,
			Map<String, ? extends Object> paramMap) {
		Long count = getCount(sql, paramMap).longValue();
		Query query = setParam(getEntityManager().createNativeQuery(sql, resultClass).setFirstResult(dgm.getStart())
				.setMaxResults(dgm.getStart() + dgm.getLength()), paramMap);
		List<T> result = query.getResultList();
		return new PageResponse<T>(result, count, dgm.getDraw());
	}

	/**
	 * 根据参数查询总数
	 * 
	 * @param jpql
	 *            查询语句
	 * @param paramMap
	 *            参数Map
	 * @return BigInteger
	 */
	public BigInteger getCount(String jpql, Map<String, ? extends Object> paramMap) {
		String countJpql = "select count(1) from (" + jpql + ") as countT ";
		Query query = setParam(getEntityManager().createNativeQuery(countJpql), paramMap);
		return (BigInteger) query.getResultList().get(0);
	}

	/**
	 * 添加参数
	 * 
	 * @param query
	 *            Query
	 * @param paramMap
	 *            参数Map
	 * @return Query
	 */
	private Query setParam(Query query, Map<String, ? extends Object> paramMap) {
		if (paramMap != null) {
			Set<String> mapSet = paramMap.keySet();
			for (String key : mapSet) {
				query.setParameter(key, paramMap.get(key));
			}
		}
		return query;
	}

	public EntityManager getEntityManager() {
		return entityManager;
	}

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

}
